═══ 1. Jul 1996 Title Page ═══ Portions copyright (c) by IQPac Inc. Volume 4, issue 6 Microsoft Does it Again It's like the ole' one-two body punch that you know is coming but can't do anything about. Doesn't it sometimes seem like no one wants to do anything about it, though? Peter Coffee reports in this week's PC Week that Microsoft is not going directly to the object-oriented file system in Cairo but will instead make constant incremental upgrades to NT to (eventually) achieve this goal. The really scary thing is that they'll get away with it again. Just like they'll get away with the ActiveX confusion that they've created. According to the opinions of the trade press, once the version of Visual Basic is released that generates ActiveX controls, no one will remember how bad it really is. I can relate - I had thought Basic was dead and buried until Microsoft resurrected it and (worse yet) made it one of the most popular languages in the "I'm not really a geek" market. PC Expo is Here Again All of this hoopla can only mean one thing (actually, it could mean several, but that sounded good so I went with it) - PC Expo is back in town. I'm beginning to wonder if I shouldn't apply for a hotdog vendor's license. Anyone who has attended this show knows what I'm talking about - this is the only week when hotdogs on the West Side cost $2.00 each, and that's the bargain price! The show should be good, though. There is supposedly a separate section just for the Internet and related technologies, which I am looking forward to browsing. Also, who can forget all of the goodies, although the quality has been declining in recent years (as I have been told. I haven't attended the past two shows.) Anyway, if you're planning on being there, send me email and we'll meet somehow. Cast Your Ballots! Speaking of sending me email, I hope to soon have a working HTML form that you can use to vote on your favorite article over the past months. If I don't have it done soon, I will have to revert to email for the ballots. [sigh] We have three prizes already generously donated by IBM which will go to the winners. These aren't pledges - I have them here in my "toy room" (still shrink-wrapped, I will proudly add). I will not yet divulge what they are, however. Our First Advertisement EDM has its first victory! We are happy to welcome SPG to the (now-growing) list of EDM sponsors. My Absence I've been absent the past week. I apologize, but I plead hard drive trouble as the cause. It started last week with the corruption of the "EA DATA . SF" file's FAT entry on my boot drive. This means, for those who haven't picked up on it yet, that my desktop was lost totally. I didn't do anything to cause this; it just happened sometime between the time I turned off the computer last Sunday and the time I turned it on again the next evening. The next night, the Boot Manager partition became the active one that was booted - a feature I disabled a long time ago. The partition name was missing, too. That was enough for me. I booted to the command line, backed everything of value up to the ZIP drive that I wrote about a few weeks ago, repartitioned, reformatted, and reinstalled. (Un)fortunately, my version of Warp is the original, pre-CONFIG.SYS/AUTOEXEC.BAT bug-fix, red box version that was distributed to the press (who subsequently ate it for breakfast), and I didn't want to reinstall that monster, so I conveniently used this as an excuse to upgrade to Warp Connect. Oh, and I forgot to make a bootable DOS floppy (I need my real-time MIDI recording capabilities), so I just had to buy DOS 7.0. All-in-all, the day (Wednesday) wasn't so bad. Voting is On! Then, on Thursday, I discovered the bug that has been plaguing me since the beginning of May; I fixed it and the result is that you are now able to vote for your favorite articles. The winners get big prizes, so cast your vote today! Other changes on the site include the much-overlooked mistake that the Back Issues and the Links pages were the same. Worse yet, I made this mistake locally, so I didn't have a backup to fall upon. (I did have a sword, but that's so messy.) We now have a growing list of links and are always looking for more. The Acknowledgments and Sponsors pages have been combined. This only makes sense. The Real World But, enough of us. What's going on out there?  Another journalist has the solution to IBM's troubles - "What IBM really needs to do is..." How many times have you heard this? I'll finish that sentence by saying that IBM really needs to tell these people to shut up ("or something...huhuhuhuh"). Only when the ambient noise has been eliminated can they concentrate on solving their problems.  IBM announces that it's buying Microsoft - just seeing if you're awake. [grin]  Merlin gains another feature - Infoworld likened Merlin to the Swiss Army knife, an analogy that is not ill-fitted at all. IBM announced plans to include Notes Mail with the new version of OS/2, the first beta of which will ship next week. This new version will not run any custom Notes applications, however. Quick! Everyone Do the Hokey Pokey! Blah blah blah. I suppose it's that "time of the year" again. Some "high priced consultant to IBM" said, according to Robert X. Cringely, that Merlin better sell well or OS/2 is toast. I have a very nasty suspicion that I know this person well, especially since he fits that description and is always hooting about how OS/2 is doomed. He doesn't mind taking their money, however. Heh. Maybe he should become the "people's poet" by quitting his position so that IBM can put an extra dollar or two into the OS/2 coffers. "Well, gee, Wally. Redmond says everyone's moving to NT and this here high priced consultant says that OS/2 is doomed, so I guess, yuck yuck, that we should also do the 'hokey pokey.'" That's us. Just call us "idiot's on a rope." OS2Web is Back Up! After the "Pe-Perlow Fi-asco" (that even has the proper rhythm), Jeff "Boomer" Bakalchuck (I hope I spelled his name properly) has gotten everything in order and the sight is once again at "full steam ahead." (Aye, aye, Captain.) If you haven't visited in a while, the new look will pleasantly surprise you. Stop by at http://www.teamos2.org/os2web. From the IBM School of Marketing It seems that Rogue Wave took that IBM route when it comes to marketing. "Yessirree, yuck yuck, we'll build a better mousetrap and they'll come a runnin'." I'll bet that you don't know that Rogue Wave has an OS/2 Java development environment already on the market. Yeah, neither did we. We have the name and number of someone at Rogue Wave that we hope to speak with shortly about this. Look for a "First Looks" on this hopefully coming in an issue soon. PC Expo I'm really surprised that no one asked me anything about PC Expo. I daresay that if you weren't there, you didn't care; this would explain the lack of interest. My beef at the show was to drum up support for EDM/2, including selling advertising space, acquiring software for review, and generally making myself known. Of course, I could've showed up in a katt outfit, but I didn't want to be too conspicuous. "So," you ask me, "what did you see?" Well, when I wasn't trying to hobnob with the various product managers and such, I did manage to see... Merlin This was the hottest thing for OS/2 enthusiasts. Aside from the (amazing) jugglers that were there, I hung around until late that afternoon for a more intimate demo of the system. Since there was only one other person watching, I got to ask questions about the system, what the components were like, etc. It turns out that the task bar, a la Object Desktop, was "stolen" (almost a direct quote from the IBM gentleman) from the Lotus Smartsuite. "I hope they didn't pay the billions of dollars for that alone," queried the quantifying Q-ster. (Heh, Katt doesn't know how easy he has things!) The interface, however, was rather stunning and it will likely be the most noticeable improvement over Warp. No, I haven't forgotten VoiceType, but I don't yet believe that the majority of people are going to use it. Let's face it - if everyone went for the new and better technology immediately, OS/2 would have smashed Windows into the ground a long time ago. No, people are very inertial, especially when dealing with technology they don't quite yet understand. VoiceType I don't want to detract from VoiceType, though. It worked fairly well, although the demoers - is that a word? - did at times have trouble with it. For the record, it was hard-to-tell if the problems were caused by the software or the extreme amount of ambient noise in the large conference center. I am looking forward to playing with it; hell, if it's good enough, maybe I won't have to type in this column every week. I still have to get one of those nifty headset microphones before I can use it. (It figures. I have over $20,000 of musical equipment sitting next to my computer, but I don't have a headset microphone.) TeamConnection This is a project management software package which runs on OS/2 using TCP/IP as the network protocol for a truly client/server system allowing multiple people to work on a large project simultaneously. IBM rattled off a whole list of features to me, but I opted for the evaluation copy of the software. Hopefully, we'll get the real McCoy and Brad will be able to review it in the near future. One thing we won't be able to test (I don't think so, at least) but is something you should definately be interested in is distributed building of your product. You have 30 computers connected via a network - why should only one of them do all of the compiling? This feature definately sounds fun to watch in action. Visual Age For those of you who are interested in cross-platform development, IBM's Visual Age product was being demoed on OS/2 and NT with the same user interface (for the most part). If you've complained about the performance of the Visual Builder (who hasn't?), some help is on the way. According to the gentlemen I spoke to, IBM segregated the code generator from the builder and (if I remember correctly) rewrote the generator in C. The visual aspects of the builder are still written in Smalltalk, I infered. Where Was Everyone Else? Besides IBM and Lotus, there was a small section of IBM's floor space dedicated to ISV's. DevTech is the only ISV whose name I can remember, though. This is getting scary, folks. Where was everyone else? I was really disappointed to find (or not find, as the case may be) little OS/2 software outside the confines of the IBM and Lotus sections. When I say little, I mean little. There was so little software for OS/2 that I can't even remember seeing any, but something inside tells me that I did indeed see some OS/2 packages. This is yet another testimony to the lack of support for OS/2 in the marketplace. While I don't completely agree with the pronouncement of doom, OS/2 is quickly headed for the ranks of the Amiga OS and Unix, i.e. well known, but not popular among the mainstream users. ═══ 2. Copyright Notice ═══ Copyright Notice EDM/2 is published by IQPac Inc. IQPac Inc. can be reached via U.S. Mail at the following address: IQPac Inc. 7 East Broadway, Box 804 New York, NY 10038 U.S.A. Editor-in-chief Larry Salomon Jr. (larry_salomon@iqpac.com) Associate editor Carsten Whimster (carsten_whimster@iqpac.com) Contributing editor Brad Scharf (brad_scharf@iqpac.com) Contributing editor Joe Wyatt (joe_wyatt@iqpac.com) Contributing editor Gordon Zeglinski (gordon_zeglinski@iqpac.com) CEO/President Larry Salomon Jr. (larry_salomon@iqpac.com) All material is copyrighted by its original author. No part of this magazine may be reproduced without permission from the original author. This publication may be freely distributed in electronic form provided that all parts are present in their original unmodified form. A reasonable fee may be charged for the physical act of distribution; no fee may be charged for the publication itself. Neither IQPac Inc. nor this publication are affiliated with International Business Machines Corporation. OS/2 is a registered trademark of International Business Machines Corporation. Other trademarks are property of their respective owners. Any mention of a product in this publication does not constitute an endorsement or affiliation unless specifically stated in the text. The OS/2 Accredited Logo is a trademark of International Business Machines Corporation and is used by IQPac Inc. under license. This On-line Publication is independently produced by IQPac Inc. and IBM is not responsible in any way for its contents. IQPac Inc. is an accredited member of the IBM Independent Vendor League. Copyright Notice - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 3. How Do I Get EDM/2? ═══ How Do I Get EDM/2? EDM/2 can be obtained in any of the following ways: Anonymous FTP on the Internet hobbes.nmsu.edu in the /os2/newsltr directory. Elsewhere on the Internet generalhq.pc.cc.cmu.edu in the /pub/newsletters/edm2 directory. The EDM/2 mailing list. Send an empty message to edm2-info@knex.mind.org to receive a file containing (among other things) instructions for subscribing to EDM/2. On Compuserve All back issues are available in the forum OS2DF2, section 15. On BBS's From BBS's in Denmark, send email to Jesper Nielsen at jesniels@internet.dk for a location near you. How do I Get EDM/2? - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 4. Watermarking Your Printouts ═══ ═══ 4.1. Watermarking Your Printouts ═══ Watermarking Your Printouts Written by Larry Salomon, Jr. Introduction In the "how did they do that" category, watermarking is a concept that looks easy but, for those with little graphics experience (like me), is more difficult than apparent at first glance. For those who are not familiar with the term "watermark," it is a textual element that has the following properties:  It is drawn behind all other elements on a printed page.  It is unobtrusive so that it does detract from the reader's ability to easily read what else is on the page  It is rotated at some arbitrary angle, usually spanning the page from the lower left corner to the upper right corner See figure 1 for an example of a watermark. ──────────────────── ──────────────────── Figure 1) Sample watermark Watermarking is usually performed to indicate a security level of a document ("Confidential") or to indicate that the printout comes from a demo or unregistered version of an application. There are, however, many other possible uses which you will discover. This article will discuss how watermarking can be done in OS/2 and will briefly cover topics such as color use, fonts, graphics boundary determination, and rotational transformations. Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.2. How Would You Do It? ═══ How Would You Do It? Before we begin coding, we should consider a high-level design so that we know in what direction we are travelling; this will serve as our flowchart for the function which will do the watermarking. Our first decision will be to determine what the interface is. This allows us to consider in advance the flexibility that we will offer the application developer using this function and design with this flexibility in mind. Since watermarking can be used in a variety of situations, the text to be used should be specified by the caller. The angle of rotation should also be specified by the caller to give more control over the look of the watermark. Finally, we need the handle of the presentation space to be used. We have, therefore, the following prototype: ──────────────────── BOOL doWatermark(HPS hpsPrn,PCHAR pchText,USHORT usAngle); ──────────────────── As a corollary exercise to designing the prototype, let us consider any limitations and/or restrictions that we will impose.  As we will see, rotations can only be done on a "normal" presentation space, so the HPS must have been created with the GPIT_NORMAL flag in the call to GpiCreatePS().  The presentation space must be associated with a printer device.  The printer driver and/or device must support color or dithering (i.e. no monochrome printers)  If we were to rotate the text more than 90 degrees, it wouldn't be easily readable, so the angle should be in the range 0-90 inclusive. Now we may proceed to determining the procedure for drawing the watermark. Consider the following problem: we want the text to be as large as possible (without having it clipped on the printed page) but we cannot determine, at compile time, the size of each character. How do we calculate this at run time? The solution is easy to describe: we set the size of the character box to some value that we know to be too big and then draw the text; if the text is too big, we reduce the size of the character box by some amount and repeat. When the text completely fits within the printed page, we stop looping. Sounds easy, huh? Since this is the only "difficult" part of the design, we can now describe the procedure as such: 1. Indicate a rotation of the number of degrees specified. 2. Set the font to something "nice." 3. Set the character size to be very large. 4. Draw the text. 5. If the text is too big, reduce the character size by some amount and goto step 4. 6. Set the text color to be very light. 7. Draw the text using the calculated character size. 8. Restore the font to the one originally in use. This procedure will work, as we will see. For those of you sharp enough to catch the caveat that drawing the text in the determination part will obliterate the printout, keep reading. Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.3. Colors ═══ Colors Although the Gpi subsystem added the concept of a color palette, I have never investigated whether or not palettes will work with hardcopy devices. To save myself the aggrevation, I decided to stick with logical color tables in RGB mode, which amounts to the same thing. Logical color tables are a dated concept that worked well when the majority of displays were EGA or VGA and dithering had to be used to simulate the colors that we really wanted. Now that SVGA is considered standard equipment, logical color tables are considered obsolete. In other words, I probably doomed myself by using them. They work for the moment, however, so I'll use them anyway. A logical color table is a table of colors which is requested by the application for use within a presentation space. Gpi maps these "logical" colors to the "physical" colors defined by the hardware palette. For those colors that cannot by described adequately, Gpi uses dithering (alternating two or three colors in a 2x2 or 3x3 matrix) to simulate the color requested. These are normally used when the default RGB values for the CLR_ constants are not good enough and also when you want to specify the RGB value directly in the Gpi calls. We need the latter capability, so let's tell the Gpi subsystem this: ──────────────────── #define DW_TEXT_COLOR 0x00CCCCCC else if (!GpiCreateLogColorTable(hpsPrn,0,LCOLF_RGB,0,0,NULL)) ; else if (!GpiSetColor(hpsPrn,DW_TEXT_COLOR)) ; ──────────────────── Figure 2) Switching to RGB mode. GpiCreateLogColorTable() takes the following parameters: ──────────────────── BOOL GpiCreateLogColorTable(HPS hpsHps, ULONG ulFlags, LONG lMode, LONG lStart, LONG lCount, PLONG plTable); ──────────────────── hpsHps specifies the presentation space. ulFlags specifies zero or more LCOL_ flags and is unused by us. lMode specifies the mode of the color table and affects how the remaining parameters are interpreted and may be one of the following:  LCOLF_DEFAULT - returns the color table to the default mode, which is indexed. This uses the CLR_ constants that we all know and love.  LCOLF_INDRGB - indicates that plTable should be interpreted as an array of pairs of LONG values, where the first LONG is the index into the color table where the second LONG (which is an RGB value) is to be placed.  LCOLF_CONSECRGB - indicates that plTable specifies an array of LONG values (which is lCount large and contains RGB values) which replace the corresponding entries in the color table beginning at lStart.  LCOLF_RGB - indicates that lStart, lCount, and plTable are to be ignored and that RGB values will be used in any Gpi call that requires a color. Note how we specify a very light gray for the color (0xCC is used for the red, green, and blue components of the color). The results in almost transparent text. Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.4. Fonts ═══ Fonts In his book, Charles Petzold described a nice, generic way to enumerate the fonts on a system and set the current font to be one of your own choosing. While this is great for the general case and gleefully covers the situation when a font is not installed on a system, it is my opinion that, if you need one of the ATM fonts shipped with the system, this is too much work. Given the awe that high-quality graphics generate and the ever-shrinking cost of disk space, it is my claim that few people (if any) will choose not to install all of the fonts that OS/2 includes. With this in mind, I now present the "poor man's method of selecting an ATM font:" initialize a FATTRS structure with zeros (except for three fields), call GpiCreateLogFont(), and then call GpiSetCharSet(). Done. See how easy that was? Let's look at the details: ──────────────────── faFont.usRecordLength=sizeof(faFont); faFont.fsSelection=0; faFont.lMatch=0; strcpy(faFont.szFacename,"Helvetica"); faFont.idRegistry=0; faFont.usCodePage=GpiQueryCp(hpsPrn); faFont.lMaxBaselineExt=0; faFont.lAveCharWidth=0; faFont.fsType=0; faFont.fsFontUse=FATTR_FONTUSE_OUTLINE | FATTR_FONTUSE_TRANSFORMABLE; ──────────────────── Figure 3) Initializing the FATTRS structure. As you can see, the only three fields that we initialize are the font name (FATTRS.szFacename), the codepage (FATTRS.usCodePage), and the flags describing what type of font we want (FATTRS.usFontUse). As you can also see above, initializing these fields is trivial. The flags FONTUSE_TRANSFORMABLE and FONTUSE_OUTLINE indicate that we want a font that is "transformable" (i.e. rotatable and resizable) and is an ATM font, respectively. Shown below is the code to actually acquire the font for use. ──────────────────── #define DW_FONT_HELV 1 //---------------------------------------------------------------------- // Create the logical font. //---------------------------------------------------------------------- else if (GpiCreateLogFont(hpsPrn,NULL,DW_FONT_HELV,&faFont)==GPI_ERROR) ; //---------------------------------------------------------------------- // Set the font into the HPS. //---------------------------------------------------------------------- else if (!GpiSetCharSet(hpsPrn,DW_FONT_HELV)) ; ──────────────────── Figure 4) Creating the logical font and setting it is the current font. GpiCreateLogFont() creates an association between the HPS and the physical font. It takes the following parameters: ──────────────────── LONG GpiCreateLogFont(HPS hpsHps, PSTR8 psName, LONG lFontId, PFATTRS pfaAttrs); ──────────────────── hpsHps specifies the presentation space in which a logical font which represents the physical font will be created. psName is used for creating SAA-conforming metafiles and is not used normally (specify NULL for this). lFontId is the identifier by which the font will be known and must be unique for this HPS. pfaAttrs points to the FATTRS structure which describes the font we want. It returns FONT_MATCH if the font was found, FONT_DEFAULT if the font was not found but a substitute font was, or GPI_ERROR if an error occurred. GpiSetCharSet() specifies the current font to be used and takes the following parameters. ──────────────────── BOOL GpiSetCharSet(HPS hpsHps,LONG lFontId); ──────────────────── hpsHps specifies the presentation space. lFontId is the identifier of the font used in the GpiCreateLogFont() call. Now that we have the font we want, we have to know how to specify the size that we want. This only pertains to transformable fonts, which is why we specified FONTUSE_TRANSFORMABLE in the FATTRS structure. Fixed Decimal Arithmetic When the Gpi subsystem was developed, floating point processors were not part of the CPU, nor were they present as separate chips in the majority of computers used at that time. So, to avoid the penalty of emulating floating point operations in software, the Gpi developers decided to use fixed point arithmetic when non-integral numbers were needed. For those of you unfamiliar with the concept, fixed point arithmetic implies a decimal point, even though one is not actually specified in the number. The Gpi subsystem adds a twist to this, however. Instead of using base-10, it uses base-10 only for the integer part of the number and base-64K for the fractional part. In other words, if you have fixed point number i.f, you convert this to a floating point number using the formula i+f/64K. This denominator was not chosen to be sadistic. You will remember that 64K-1 is the maximum value expressable in 16- bits, which allows us to imply the denominator and yet get a complete range of fractional values. If you restrict the integer portion to 16-bits also, the total number fits nicely in a ULONG. Why is all of this relevant? Because character sizes are specified using fixed-point numbers, of course. Let me now introduce the SIZEF structure which is similar to the SIZEL structure except that the cx and cy fields are of type FIXED instead of LONG. Yes, yes, they amount to the same thing, but it's the principle that matters. The only other piece of information that you need to know is that fonts are designed such that they display with no distortion if and only if the horizontal component of the character size is equal to the vertical component. (The box that has the dimensions of the character size is known as the "character box.") The code to set the character box (initially) is shown below. Note the (necessary) call to GpiSetCharMode() which tells Gpi that we want to rotate text in the presentation space. ──────────────────── //---------------------------------------------------------------------- // Get the HPS dimensions. GpiQueryPS() does not have an error return // code defined. //---------------------------------------------------------------------- GpiQueryPS(hpsPrn,&szlHps); szfChar.cx=MAKEFIXED(szlHps.cy/2,0); szfChar.cy=szfChar.cx; : : //---------------------------------------------------------------------- // Set the character mode to 3 so that we can rotate them. //---------------------------------------------------------------------- else if (!GpiSetCharMode(hpsPrn,CM_MODE3)) ; else if (!GpiSetCharBox(hpsPrn,&szfChar)) ; ──────────────────── Figure 5) Setting the character box to its initial value. Again, see how easy this is? And you thought fonts were difficult! Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.5. Transformations ═══ Transformations One class that I had to take as a Computer Science major which was both interesting and boring at the same time was Linear Algebra. In this class we learned everything about matrices we ever wanted to know and even some stuff that we never wanted to know. (How many of you actually used your knowledge of Eigen vectors after graduating?) One interesting application of matrices is transformations. In a nutshell, by properly initializing a matrix of the appropriate size, one can scale, rotate, shear, or even map a point in 3-D space to 2-D space. The Gpi subsystem provides two ways of doing transformations, just as it does fonts: the hard way and the easy way. Because we'll never finish this article if we describe the hard way, we'll use the easy way. As with fonts, the easy way doesn't provide the same flexibility as the hard way, but we don't need to perform all sorts of fancy transformations either. Just a simple rotation would be sufficient. Fortunately, the easy way also includes a function to do this for us: GpiRotate(). It applies a rotation of an arbitrary angle to an entity called (as you would imagine) a transformation matrix. ──────────────────── BOOL GpiRotate(HPS hpsHps, PMATRIXLF pmlMatrix, LONG lFlags, FIXED fAngle, PPOINTL pptlOrigin); ──────────────────── hpsHps specifies the presentation space. pmlMatrix points to the transformation matrix, which we will not describe here. lFlags specifies how the rotation is to be performed. fAngle specifies the angle of rotation in degrees. pptlOrigin specifies the point around which the rotation is to be performed. lFlags can be one of the following values:  TRANSFORM_REPLACE - specifies that the result is to replace the transformation already specifed in pmlMatrix.  TRANSFORM_ADD - specifies that the rotation is to be applied after the transformation specified in pmlMatrix.  TRANSFORM_PREEMPT - specifies that the rotation is to be applied before the transformation specified in pmlMatrix. The resulting transformation matrix is returned in pmlMatrix regardless of the option specified. What does this do for us? When the resulting transformation matrix is applied to the HPS, it allows us to draw using a coordinate system oriented in the usual fashion (i.e. the x axis is horizontal and increases to the right and the y axis is vertical and increases upward). The output, however, is rotated by the angle specified. The Gpi subsystem provides many layers of transformation, and we will not describe them here because they are too advanced for the scope of this article (read: I don't understand them myself). Suffice it to say that the function we need to effect the transformation represented by pmlMatrix is GpiSetModelTransformMatrix(). It takes the following parameters: ──────────────────── BOOL GpiSetModelTransformMatrix(HPS hpsHps, LONG lInitFields, PMATRIXLF pmlMatrix, LONG lFlags); ──────────────────── hpsHps specifies the presentation space. lInitFields is the number of fields in pmlMatrix that are initialized and, for us, will always be 9 (meaning that a transformation matrix has size 3x3). pmlMatrix specifies the transform to be applied. lFlags specifies how the transformation is to be applied and can have the same value as the equivalent parameter in GpiRotate(). The code to set the rotation is shown below: ──────────────────── //---------------------------------------------------------------------- // Rotate about the center of the page. //---------------------------------------------------------------------- else if (!GpiRotate(hpsPrn, &mxRotate, TRANSFORM_REPLACE, MAKEFIXED(usAngle,0), &ptlPoint)) ; else if (!GpiSetModelTransformMatrix(hpsPrn, 9, &mxRotate, TRANSFORM_REPLACE)) ; ──────────────────── Figure 6) Setting up the rotation. Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.6. Graphics Boundaries ═══ Graphics Boundaries The title of this section implies that it is possible to determine the boundaries of any given set of graphics commands. Since it would be useless otherwise, it makes sense to assume that it is also possible to do so without affecting the current output in the HPS. There is a little known, but powerful, Gpi function which performs both of these actions: GpiSetDrawControl(). It, in a general sense, controls whether or not specific functionality in the Gpi is in effect. (Yes, this is confusing. Keep reading and it should make sense.) It takes the following parameters: ──────────────────── BOOL GpiSetDrawControl(HPS hpsHps,LONG lControl,LONG lValue); ──────────────────── hpsHps specifies the presentation space. lControl specifies a DCTL_ constant indicating which attribute of the Gpi subsystem you which to change. lValue specifies DCTL_ON or DCTL_OFF to turn the attribute on or off, respectively. The two values of lControl that we're interested in are DCTL_DISPLAY and DCTL_BOUNDARY: DCTL_DISPLAY specifies whether or not anything is to be drawn on the output device. With the exception of GpiErase() any drawing function results in a NOP if DCTL_OFF is specified. DCTL_BOUNDARY specifies if the Gpi subsystem should keep track of the extents of the units (pels, low English, etc.) that were affected by a drawing command. This information can be retrieved using GpiQueryBoundaryData(). It is not affected by the state of the DCTL_DISPLAY attribute. You should already be able to see how we are going to use this function. ──────────────────── //---------------------------------------------------------------------- // Turn off drawing and turn on boundary collection //---------------------------------------------------------------------- else if (!GpiSetDrawControl(hpsPrn,DCTL_DISPLAY,DCTL_OFF)) ; else if (!GpiSetDrawControl(hpsPrn,DCTL_BOUNDARY,DCTL_ON)) ; : : //------------------------------------------------------------------- // Turn on drawing and turn off boundary collection //------------------------------------------------------------------- else if (!GpiSetDrawControl(hpsPrn,DCTL_DISPLAY,DCTL_ON)) ; else if (!GpiSetDrawControl(hpsPrn,DCTL_BOUNDARY,DCTL_OFF)) ; ──────────────────── Figure 7) Setting the drawing and boundary data collection attributes. The code above allows us to, inside the bracket, determine the character box size for the watermark. This stuff is so easy, you should be writing this article instead of me. Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 4.7. The Result ═══ The Result We now have enough knowledge to successfully implement the design that we deduced earlier. The function as well as the application which calls the function are in the sample source code that accompanies this article. Although you should be able to understand the code competely, any questions or comments are welcome via email; my address is larry_salomon@iqpac.com . Enjoy! Watermarking Your Printouts - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 5. A Progress-indicating Status Line in C++ (Part 2) ═══ ═══ 5.1. A Progress-indicating Status Line in C++ (Part 2) ═══ A Progress-indicating Status Line in C++ (Part 2) Written by Stefan Ruck Introduction In the first part of this small series, published in EDM/2 Volume 4, Issue 5, I introduced the progress-indicating status line and the needed message handler to you. This time I will show you a simple base file class which is wrapped around the API-calls and a derived class which sends status messages to a given status line. A Progress-indicating Status Line in C++ (Part 2) - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 5.2. The Base File Class ═══ The Base File Class The primary purpose of this class named AFile is to hide the main file-handling API functions. It just covers opening, reading, writing and of course closing a file. Let's have a look at its member. The class AFile is defined in afile.hpp and afile.cpp The Constructor Member There is only one constructor which takes two arguments, pBuffer (a PVOID) and ulBufferSize (a ULONG). They are initialized to zero by default. What are these arguments good for? If you know the buffer you want to read the file's contents into, and it's size, you can pass a pointer to the buffer and the buffer's size to the file object as soon as you create it. When you do any operation (reading, writing) later on this file you don't have to pass the buffer and its size anymore because they are already known. This may be useful, e.g. for configuration files. The members which store this information are declared as const so they can't be changed after construction. The open Members The difference between the two AFile::open members is the way you pass the filename. It can be a pointer to a string or a reference to an IString object. The other arguments are similar to the DosOpen function. The only difference is that the file handle is not needed because it's a private member of the file object. The AFile::reOpen member can only be used when a file object was opened before and is actually closed. Because the filename of a AFile object is kept in a private member you don't have to pass it again when you re-open the same file using the same object. The Close Member This member is implemented for closing the file during the lifetime of the file object. Destroying a file object always closes the file. The read and write Members The default value of both parameters (PVOID pBuffer and ULONG ulBufferSize) is 0L. If you call AFile::read or AFile::write without passing a pointer to the buffer and/or the buffer's size the values passed to the constructor are given to the DOS-API-functions. In addition to the OS/2 API return-values there are three more return-values:  AFILE_FILE_NOT_OPEN is returned in case the file connected to the object is not open.  AFILE_NO_BUFFER_POINTER indicates that no pointer to a buffer is passed to the constructor or to the member. And AFILE_NO_BUFFER_SIZE means that the size of the buffer is missing. The Refresh-Flag on AFile::getFileSize This flag is used internally when the AFile::queryFileInfo member is called. If it is set to true DosQueryFileInfo is called even if nothing about the file has changed. If it is set to false DosQueryFileInfo is only called when it was not called before (AFile::m_pFilestatus3, which keeps the file info, is not set in this case). You have to handle it yourself when the refresh-flag should be set. A Progress-indicating Status Line in C++ (Part 2) - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 5.3. The Message-Sending File Class ═══ The Message-Sending File Class The main purpose of this article is to present a class which uses the progress-indicating status line to show the state of a process. This class is AEditFile. It is defined in editfile.hpp and editfile.cpp. We can't use AFile directly because the methods AFile::read and AFile::write do not return until they have finished and the whole file is read / written. So what we need now are methods which read/write the data not in one big step but in a few small ones. They are AFileEdit::read and AFileEdit::write. The class AFile is the base class of AEditFile, which posts MYM_STATUS_PROCEED messages (I hope you remember this message) to the status line while reading/writing a file. The differences between this class and AFile are the members for reading/writing, and the fact that this class keeps the file's contents itself. So you can't pass a pointer to the buffer or the buffer's size to the constructor. The size of the file can't be changed. This class doesn't work on files of no size. I think there is no explanation needed for the constructor and destructor. The read Member ──────────────────── //************************************************************************** // AEditFile :: read - read the data into the buffer * //************************************************************************** APIRET AEditFile :: read (HWND hwndStatusLine) { ULONG ulBytesLeftToRead = m_ulFileSize; // set the variables ULONG ulReadAtBufferPos = 0; APIRET ret; if(!m_pFileContents) // no buffer allocated? throw m_pFileContents; // throw an exception m_bStopProcess = false; // do not stop the process while(ulBytesLeftToRead) // as long as there are still some bytes left to read { ret = AFile :: read (m_pFileContents + ulReadAtBufferPos, // read a block of data (ulBytesLeftToRead > (ULONG) eBytesToRead ? eBytesToRead : ulBytesLeftToRead)); if(ret && ret != ERROR_MORE_DATA) // a serious error occured? break; // yes, break the loop if(m_bStopProcess) // stop the process? { // yes m_bStopProcess = false; // reset the flag return (ERROR_MORE_DATA); // return } if(hwndStatusLine) // is a handle of a status line passed? WinPostMsg (hwndStatusLine, MYM_STATUS_PROCEED, 0, 0); // yes, post the message ulBytesLeftToRead -= getBytesRead (); // decrease the number of bytes left to read ulReadAtBufferPos += getBytesRead (); // increase the position where to place the data } return (ret); } Figure 1) AEditFile::read AEditFile holds its own buffer (see "The allocateDataBuffer Member" later in this article). So there's no pointer to a buffer or the buffer size needed as a parameter. But this member needs the HWND of the status line which indicates the progress. Because we read the data in (maybe) several steps, we must compute the number of bytes left to read (stored in ulBytesLeftToRead). And we have to keep the position to append the new data block in the buffer. This value is kept by ulReadAtBufferPos. If the buffer was not allocated by AEditFile::allocateDataBuffer, an exception of type char * is thrown. As you might remember we want to use this member to read a file inside of a multi-threaded environment. Setting the m_bStopProcess to true using AEditFile::stopProcess, the main thread is able to terminate the operation without terminating the sub thread e.g. by calling IThread::stop (which should only be used in cases of "abnormal termination". For further information refer to Leong, Law, Love, Tsuji, Olson, "OS/2 C++ Class Library, Power GUI Programming with C Set++", New York, 1993, John Wiley & Sons, p. 533f). AEditFile::stopProcess is useful e.g. if the user closes the program or wants to abort the process by pressing ESC. If the process is stopped by setting AEditFile::m_bStopProcess to true, AEditFile::read returns ERROR_MORE_DATA. AEditFile::read uses AFile::read to read the data. But it calls AFile::read several times, depending on the file's size. Every time AFile::read is called, a block of AEditFile::bytesToRead or ulBytesLeftToRead bytes, depending on the amount of bytes left to read, is read. As soon as a file is larger than AEditFile::bytesToRead, it will be read in several steps. Every time a block is read, a MYM_STATUS_PROCEED message is posted to the status line. This line of code may seem a little bit cryptic to some of you: ret = AFile :: read (m_pFileContents + ulReadAt BufferPos, (ulBytesLeftToRead > (ULONG) eBytesToRead ? eBytesToRead : ulBytesLeftToRead));. Well, I use the ? operator to check if the block of data left to read (ulBytesLeftToRead) is larger than the block I want to read by default (AEditFile::eBytesToRead). If so, AEditFile::eBytesToRead bytes is passed to AFile::read, oth erwise ulBytesLeftToRead is passed. Because we may not read the whole file in at once, AFile::read may return ERROR_MORE_DATA. This is correct, so I accept this return value. Any other non-zero return-value will break the read loop. Only if a handle to a status line window is passed will the MYM_STATUS_PROCEED message be posted. Every time a block of data is read, the position to place the data inside of the buffer (ulReadAtBufferPos) is increased by bytes read (returned by AFile::getBytesRead) while the ulBytesLeftToRead is decreased by the same value. The write Member ──────────────────── //************************************************************************** // AEditFile :: write - write the data from the buffer * //************************************************************************** APIRET AEditFile :: write (HWND hwndStatusLine) { ULONG ulBytesLeftToWrite = m_ulFileSize; // set the variables ULONG ulWriteFromBufferPos = 0; APIRET ret; if(!m_pFileContents) // no buffer allocated? throw m_pFileContents; // throw an exception m_bStopProcess = false; // do not stop the process while(ulBytesLeftToWrite) // as long as there are still some bytes left to write { if((ret = AFile :: write (m_pFileContents + ulWriteFromBufferPos, // write a block of data (ulBytesLeftToWrite > (ULONG) eBytesToRead ? eBytesToRead : ulBytesLeftToWrite)))) break; // if not 0 == NO_ERROR returned, break the loop if(m_bStopProcess) // stop the process? { // yes m_bStopProcess = false; // reset the flag return (ERROR_MORE_DATA); // return } if(hwndStatusLine && ulBytesLeftToWrite) // is a handle of a status line passed? WinPostMsg (hwndStatusLine, MYM_STATUS_PROCEED, 0, 0); // yes, post the message ulBytesLeftToWrite -= getBytesWritten (); // decrease the number of bytes left to write ulWriteFromBufferPos += getBytesWritten (); // increase the (buffer-)position from where to write the data } return (ret); } Figure 2) AEditFile::write This member is similar to AEditFile::read, so I think there is just a little bit of explanation needed. The proceed message is only posted if there are bytes left to write. Otherwise the status line will display a per centage greater than 100. AEditFile::write writes the data, like DosWrite does, at the current position of the file pointer set by former read or write operations. To set the file-pointer, you have to use DosSetFilePtr or better create a AFile::setFilePtr member and use this one. I did not include such a member to AFile because I never keep a file open while editing it. And on every file open the file-pointer is set to the beginning of the file. The allocateDataBuffer Member This member is used to allocate the memory which keeps the file's contents. You should not call it before the file itself is open because the file size is needed. If the memory is allocated successfully or a block of memory is already allocated, it returns true. If the file is not open, it returns false. It throws an exception of type ULONG if the size of the file is 0 or the size can not be retrieved. An exception of type char * is thrown if the memory allocation itself fails. So this member should be used always inside a try/catch block. A Progress-indicating Status Line in C++ (Part 2) - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 5.4. Concluding Remarks ═══ Concluding Remarks Neither of the classes nor AStatusLine have a copy constructor. Because some data members are just pointers to a memory block allocated during runtime, you must not copy objects of these classes. The reason is that the default copy constructor can't handle those members in the correct way. In AFile, there are some members set but never used. I included them for future methods, e.g like getOpenFlags, getOpenMode. You might miss the two messages left defined in Part I, MYM_PROCESS_START and MYM_PROCESS_END. In principle they can be posted by AEditFile::read and AEditFile::write, but I think they should be posted by the main object which starts the process because one of the values passed by the MYM_PROCESS_START message is a resource id. If you want to use the AEditFile class in several projects and use AEditFile::read and AEditFile::write for posting the start message, you must take care that there is always a resource defined which matches the resource id passed in this members. The main object, which uses the classes shown in part I and part II in a (really) simple multi-threaded environment, will be the contents of the last part. A Progress-indicating Status Line in C++ (Part 2) - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6. The Codesmith's Library ═══ ═══ 6.1. The Codesmith's Library ═══ The Codesmith's Library Written by Joe Wyatt ──────────────────── ──────────────────── In The Codesmith's Library, I focus on development books and materials. I write this column from the point of view of an intermediate PM C programmer and intermediate REXX programmer. Pick up whichever book strikes your fancy, and join the growing group of people following our PM programming columns. I have already reviewed a number of beginner books, and will try to concentrate more on intermediate techniques and special topics from now on. Please send me your comments and thoughts so that I can make this column what you want it to be. I read and respond to all mail. Message Functions, OS/2 Quick Reference Library Volume 2 is possibly the only book on the market which covers all the OS/2 messages. It is volume 2 of a 6 volume series. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6.2. Errata ═══ Errata Yet again I am going to trumpet The OS/2 API Project. It is getting quite useful, with more than 36 APIs on site, as well as over 75 #define's. Have a look, and if you have the time, chip in by writing up one or more APIs and sending them to me. I will also happily listen to any and all suggestions regarding layout and format. The more people help out, the quicker this project will become very helpful. The INF version is now out, although it is a touch behind the HTML version. Check it out at [Editor's note: EDM/2 has "absorbed" the OS/2 API Project, although it continues to be a separate entity. It's location has changed, however, and it is reflected in the URL below.]: http://www.iqpac.com/edm2/os2api/index.html The OS/2 API Project aims at documenting the entire OS/2 API and distributing it as a free document, to help out people who can't afford the IBM documentation, or have something better to spend their money on. I figure it can only help the number of OS/2 programs out there if more people have access to this information. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6.3. OS/2 Quick Reference Library, Volume 2, Message Functions ═══ OS/2 Quick Reference Library, Volume 2, Message Functions This is the second book in the same series as WIN Functions, OS/2 Quick Reference Library Volume 1, which I previously reviewed. Here are the chapter headings: 1. How to Use This Book 2. Notebook Control Messages 3. Button Control Messages 4. Combo Box Messages 5. Container Control Messages 6. Direct Manipulation Messages 7. Entry Field Messages 8. File Dialog Messages 9. Font Dialog Messages 10. Help Manager Messages 11. List Box Control Messages 12. Multiline Entry Messages 13. Menu Control Messages 14. Profile Messages 15. Scroll Bar Messages 16. Slider Control Messages 17. Static Control Messages 18. Spin Button Messages 19. Title Bar Messages 20. Value Set Control Messages 21. Window Messages Appendix A: Data Type Structures Appendix B: Error Code Definitions Appendix C: Container Notification Codes As you can see from the picture of this book, it is bound with ring binding. All reference books should be bound this way, or at least with the O'Reilly-style binding, but unfortunately this is rarely the case. `Nuff said. At the start of each chapter, there is a little section with cross-references to other related messages. Unfortunately, there are no page numbers given, only which chapter they are in. Each message has a title, a brief description, message declarations (ie. param1, param2, and reply), parameters, returns, remarks and default processing. The remarks section is where you will probably spend most of your reading time, but unfortunately, I find that it is not always complete. Many, if not most, of these messages are sent by a call to some API, but this reference does not discuss these related APIs. In addition, some special properties of certain messages are not discussed either, such as the fact that sending MM_SETDEFAULTITEMID actually converts a cascade menu to a conditional cascade menu. Even though this is a message reference, I find myself wishing for a stronger connection to the related APIs. In general, however, the descriptions are quite good, if occasionally incomplete. The layout of the chapters is logical, being alphabetical by message name though the whole book. The lack of cross-reference page-numbers is a bother, though. As an example, if you are looking for the LM_SEARCHSTRING message, you will find it on page 186. At the start of the chapter, page 175, there are references to other related messages, such as WM_CONTROL. To find this, you must then flip around until you find it on page 372. The fact that the messages are alphabetical through the whole book helps somewhat, but this is still 3 separate searches though the pages. This could have been minimized by following the same format as the OS/2 Warp API series, with page numbers listed, or even the same as the earlier book in the same series! Again, this book is not a tutorial by any means, but it does provide a description for every message I have ever come across, which is more than any other book on the market can say. I recommend this book, but despite the fact that it is competent, it misses the opportunity to go that extra mile and become the great reference book that some of its companion books are. I suspect that it will be surpassed by the next message book on the market, unless it is updated first. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6.4. Summary ═══ Summary This little reference book has the only message descriptions out there currently, and it is quite invaluable as such. Nonetheless, there are a number of convenience features missing, such as page number references, see also sections, coloured chapters (on the page ends, a la Sidekick, see volume 4 issue 2), and descriptions of related functions. It is good, but due to the lack of these items, I feel that a newer book could quickly eclipse this one. I rate it a B. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6.5. Book Reviewed ═══ Book Reviewed  Message Functions, OS/2 Quick Reference Library Volume 2, Scholin - Van Nostrand Reinhold, ISBN 0-442-01898-3, $20US, $30CAN. Note that Van Nostrand Reinhold has sold its OS/2 library to John Wiley and Sons, and hence this book will now be obtainable through John Wiley, rather than VNR. - Intended audience: PM C Programmers - Mark: B A good reference, but misses out on several convenience features. This is the only message reference on the market currently, to my knowledge, so it wins by default. NOTES Please note that books aimed at different audiences should only be compared with great care, if at all. I intend to concentrate on the strong points of the books I review, but I will point out any weaknesses in a constructive manner. LEGEND BOOK: The name of the book, and the author(s) PUBLISHING INFORMATION: Publishing company, ISBN, and approximate price. AUDIENCE: This is a description of the audience I think the book targets best. This is not intended as gospel, just a guideline for people not familiar with the book. MARK: My opinion of the success of the book's presentation, and how well it targets its audience. Technical content, accuracy, organization, readability, and quality of index all weigh heavily here, but the single most important item is how well the book covers what it says it covers. Many books try to cover too much, and get a lower mark as a result. +------+-------------------------------------------------------------------------+ | MARK | DESCRIPTION | +------+-------------------------------------------------------------------------+ | A+ | Ground-breaking, all-around outstanding book | | A | Excellent book. This is what I want to see happen a lot | | A- | Excellent book with minor flaws | | B+ | Very good book with minor flaws or omissions | | B | Good book with some flaws and omissions | | B- | Good book, but in need of improvement | | C+ | Mediocre book with some potential, but in need of some updating | | C | Mediocre book with some good sections, but badly in need of fixing | | C- | Mediocre book, little good material, desperately in need of an overhaul | | D | Don't buy this book unless you need it, and nothing else exists | | F | Don't buy this book. Period | +------+-------------------------------------------------------------------------+ COMMENTS: This is a very brief summary of the review proper. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 6.6. Coming Up ═══ Coming Up I do not yet have a book for next month. I have been promised several of the books below, however. Other books I would like to review: The Art of OS/2 Warp Programming, Panov, Salomon, Panov - I have recently noticed that this has been out for a while. Hint, hint, Larry. [Lest anyone think I am stingy, I finally got in touch with my editor - after much confusion and miscommunication - and a copy is on the way. - Larry] Objects for OS/2, Tate - This sounds interesting, but is it C or C++? Time will tell. The Design of OS/2, 2nd Edition, Kogan and Deitel - I am having trouble getting a copy of this, so if anyone can help, please do. A 1st edition would do me fine too. OS/2 C++ Class Library: Power GUI Programming with CSet++, Law, Leong, Love and Tsuji - This is on the way I am told. OS/2 Presentation Manager GPI, 2nd edition, Winn - I really would like to review this soon, as I am coming across some stuff that the old one doesn't handle in an up-to-date manner, and want to see what the new one is all about. Visual Age: Concepts and Features, ? - This sounds interesting. If anyone has a book they want to see reviewed, I will be happy to oblige. Just mail me and tell me which. Publishers can send me books at the address on my personal page at the end of the magazine, and I will review all OS/2 development-related and advanced user books I receive. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 7. REXX Inside and Out ═══ ═══ 7.1. REXX Inside and Out ═══ REXX Inside and Out Written by Carsten Whimster Introduction Several years ago I wrote programs in the SAS language to interrogate the MVS operating system's resources and configuration. Because of the interpretive nature of SAS, it was possible for one program to write new sections of code based on elements defined to the system, place them in temporary data sets, and then include them later in the program. Now instead of needing to keep static tables of resource information that were manually updated, I was able to provide a maintenance free application that could adjust itself to changes in the operating environment. I named the program SAS007 (007 as in James Bond) because of its sneaky nature. "You mean your program writes itself, executes, and then goes away so that there is no chance to debug it or see what was going on?", a co-worker asked one day while examining my program and slowly shaking her red head. "Slick, huh?", I boasted. She handed my program back, and with a chuckle she added, "I never really did like dynamically self-violating code myself." I've learned quite a bit since that time, but the old temptations return when dealing with REXX and automation. "REXX is an interpretive language, right?", I ask myself. "Why not write some of that self-violating stuff that will adapt to the variations in the system? I could stuff some dynamic code into a file and then... No WAIT! I could put a bunch of code into a buffer and invoke the INTERPRET instruction! Yeah. That's the ticket!" Usually it's about now that reality hits me and I realize that even though the language supports it, it might not be such a good idea to use it. Although we will discuss the INTERPRET instruction, this column is dedicated to the avoidance of "dynamically self-violating" code. REXX Inside and Out - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 7.2. Interpret ═══ Interpret INTERPRET is one of those statements that if you need it you have to have it, but you pray that you will never need it. At least that is what I have heard. In my seven years of REXX programming, although I have played with INTERPRET, I have never "needed" the instruction badly enough to use it in a production level program. The statement usually renders the program unmaintainable by anyone but its author unless the author has slept since the program was written. (if the author has slept since the program's conception there is a great chance that he/she will be unable to easily update the code) "Couldn't you just fully document the use of this instruction in your program so that its intended purpose is apparent?", you ask. "Well... I have to believe that anybody who would not find away around using this instruction would not bother to comment their code." (That last statement was placed in quotes because it is certain that there are people out there who have had a valid need for this instruction and have taken great pains to comment the reasons for its use. Call it a gross generality if you would like.) There is a further reason to refrain from the use of the INTERPRET instruction. The IBM compilers for REXX (no, they don't exist for OS/2 at this time and may never) do not support it. So why am I spending time telling you about an instruction that is at least as wicked as a GOTO? Can you say "Quick and Dirty programming?" I thought you could. When developing one shot tools or tools for self use this can solve some problems. While an entire program section can be placed in a buffer and used dynamically with the INTERPRET instruction, our example will only interpret one instruction at a time. ──────────────────── /* quick and dirty calculator */ x = 0 /* initialize displayed variables */ y = 0 z = 0 Say "x =" x /* display variable content and availability */ Say "y =" y Say "z =" z call charout ,"===> " /* input prompt */ pull stuff /* get equation */ do while (stuff <> "EXIT") INTERPRET stuff /* evaluate equation */ Say "x =" x Say "y =" y Say "z =" z call charout ,"===> " pull stuff end exit ──────────────────── Figure 1) A simple example of INTERPRET. The above program accepts user input such as "x = y + 56 / 8.3", evaluates the expressions with the INTERPRET instruction, displays the results, and gathers more input until the word "exit" is entered; or the word "barf" is entered; or the phrase "I'll bet you can't handle this input" is entered. This program would be quite a bit longer were it to take the time to evaluate the equation on its own instead of using INTERPRET. There is one other interesting feature of this bullet proof code that is a bit off of the topic that should be noted here. Many people desire a prompt line before their "pull" instruction. This is accomplished with the use of charout() with a null parameter for the file name. It was mentioned earlier that an entire program section could be placed into a variable and included as part of your program with the INTERPRET instruction. To accomplish this nasty habit, each statement must be delimited by a semicolon. ──────────────────── : buffer = "Var" || count "=" count"; say Var" || count INTERPRET buffer : ──────────────────── The above lines assign a value to a variable created by the text "Var" and the value of the variable "count" and display the contents of this new data. This example was included to show multiple program statements within a single INTERPRET, but also included to show a common misuse of this instruction. REXX contains a function that will perform this variable manipulation without the smoke and mirrors of INTERPRET. REXX Inside and Out - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 7.3. Value() ═══ Value() The value function has three arguments by definition: ──────────────────── target = Value( variable_name, new_value, selector ); ──────────────────── variable_name - since the name of a variable is an argument, variables can be created dynamically. The previous use of INTERPRET created a variable, "Var" || count. Value() could be used to create this variable by passing the same information in the first argument. new_value - the variable created or addressed by the first argument is assigned whatever value is referenced by this argument. selector - this is a special argument and for basic variable manipulation should contain a null value. As far as I can determine, the only valid non-null value for this argument is OS2ENVIRONMENT. When OS2ENVIRONMENT is used, the current session environment is interrogated to solve the value request. ──────────────────── current_path = Value("PATH", , "OS2ENVIRONMENT") ──────────────────── The above call to value() would retrieve the value of the session's current "PATH" environment variable but would not change its value because the new_value argument was left blank. returns - value() returns the value of the variable referenced by the first argument before the new value is assigned. The last INTERPRET example could have been replaced with the more efficient and compiler friendly ──────────────────── : call value "Var" || count, count Say value("Var" || count) : ──────────────────── A further example: ──────────────────── : drives = SysDriveMap("C:", "USED") /* get list of available DASD */ do words( drives ) parse var drives singleton drives /* loop through drive letters */ info = SysDriveInfo( singleton ) /* get disk drive information */ call value word(info, 4), word(info, 2) /* create var with disk name */ end : ──────────────────── Figure 2) A simple example of Value(). The above program segment creates a variable for each disk drive starting with C: and assigns it the value of the number of freespace bytes left on the drive. This is a less contrived use of Value(), but it still is too complex. REXX Inside and Out - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 7.4. Reality ═══ Reality While Value() is certainly a nifty little function, the only use of it that can be found in programs that leave my machine are to reference the OS/2 environment. REXX's compound variable structures are powerful enough to thwart any of my contrived "needs" for the value() function. The previous Value() example could have avoided any confusing use of the Value() function by being coded as follows: ──────────────────── : drives = sysDriveMap("C:", "USED") do words( drives ) parse var drives singleton drives info = SysDriveInfo( singleton ) drive_name = word( info, 4 ) drive_free_space.drive_name = word( info, 2 ) end : ──────────────────── Figure 3) Cleanup up the Value() example. Four out of five programmers agree that this example is clearer than the last Value() example. "But gee, Uncle Bob, aren't compound variables slow?", you might ask. REXX's variable pools are kept in efficient tree structures so that their manipulation does not slow down the program. If speed is really a concern it might be noted that this example also eliminates a function call! REXX Inside and Out - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 7.5. Conclusion ═══ Conclusion REXX is a powerful language that can support all of the bad programming practices of my youth. "Dynamically self- violating code" is even easier to create in REXX than it was in the SAS language. So why not go let all of this power go to my head and just start including INTERPRET statements and Value() calls any place I think it might be "neat"? Gather around, Kiddies, and let old Uncle Bob tell you a story. Remember SAS007? That slick piece of code ran daily for a couple of years after I left the company for which it was written. One day (ok, probably over a weekend) the version of MVS was upgraded a to a new version (MVS/XA). This version changed the location of some of the information that SAS007 was gathering. Instead of being able to make some simple code changes to update the program to run with the new operating system version it was labeled as spooky and tossed aside like a useless piece of garbage. So all of the time that the company had invested for me to write that program was wasted because it had to be written again. As we've seen here, straight forward code can even be more efficient than "slick" code. Most programmers not only desire their work to be efficient, but long lived. Let's face it. Our programs are the main tools a programmer has to make a name for himself. Slick code will make a name for the programmer. What will that name change to when the code breaks and the company that paid for its development gets to keep both pieces? REXX Inside and Out - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 8. The Codesmith's Library ═══ ═══ 8.1. The Codesmith's Library ═══ The Codesmith's Library Written by Carsten Whimster ──────────────────── ──────────────────── In The Codesmith's Library, I focus on development books and materials. I write this column from the point of view of an intermediate PM C programmer and intermediate REXX programmer. Pick up whichever book strikes your fancy, and join the growing group of people following our PM programming columns. I have already reviewed a number of beginner books, and will try to concentrate more on intermediate techniques and special topics from now on. Please send me your comments and thoughts so that I can make this column what you want it to be. I read and respond to all mail. This mini review will be an occasional column which covers development-related books that are not OS/2-specific. It will not cover books that are specific to any O/S at all, but will rather cover books of importance to the developer segment in general. Because these books are not OS/2 specific, I will keep the review shorter than a full review, and will just focus on describing the book in general strokes. Writing Solid Code is the best non-OS/2 book I have read in a long time. It focuses on the prevention of bugs. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 8.2. Writing Solid Code ═══ Writing Solid Code This book maybe one of the most important books to come out in a long time, especially right now. It focuses on how to avoid writing bugs, how to prevent bugs, and how to fix bugs properly. Here are the chapters: 1. A Hypothetical Compiler 2. Assert Yourself 3. Fortify Your Subsystems 4. Step Through Your Code 5. Candy-Machine Interfaces 6. Risky Business 7. Treacheries of the Trade 8. The Rest is Attitude Epilogue: Where Do You Go from Here? Appendix A: Coding Checklists Appendix B: Memory Logging Routines Appendix C: Answers It is interesting that this book should come from Microsoft. There have been many public statements by (sometimes anonymous) officials at Microsoft, saying things like "you only ever have to get 90% of the product working", in addition to the extraordinary bugginess of the initial release of Word 6.0, and on and on. It is interesting to note that Maguire no longer seems to work for Microsoft. I can't offer any explanation for these seeming contradictions, only suggest that this book was ignored by the programmers of Word and Windows 95, and followed strictly by the NT programmers . The book starts with a short intro, which among other things encourages the use of Hungarian Notation for variable naming. I was able to find these notations on the web fairly easily by searching for "Hungarian AND Notation" on Yahoo and Web Crawler. Chapter 1 discusses a hypothetical compiler which is able to catch all problems in your code. Maguire goes on to explain that of all the most desirable error messages, many are already implemented in modern compilers, but most people turn off this error detection because they either do not understand the message, or they code in unsafe ways and find the messages annoying. He goes on to recommend the use of lint and other tools, on a religious basis. At the end of this and all the chapters, there is a short section with questions to encourage the reader to think more about the underlying issues of the chapter. There is even a little suggested project for the reader to perform, if he or she is very enthusiastic. Chapter 2 weighs the pros and cons of the standard C library's assert, and then goes on to define a better one. This is then demonstrated with several code examples. Again, the emphasis here is on the prevention and automatic detection of bugs. Documentation of tricky code is also recommended, again with some real examples. Most of the examples in this book are taken from the real process of developing Excel, mostly on the Mac. Maguire started as a programmer for Excel, and later moved up to be a team lead. The assert routine developed by Maguire is used in a variety of different situations to detect a multitude of hard-to-spot bugs lurking in the wings of the application. Fortifying your subsystems is the theme of chapter 3. The intent here is that you should provide debug-only double- checks of important algorithms, ensure that all code runs in testing, destroying garbage, initializing memory, and so on. These checks mostly occur at debug-time only, of course, so as to avoid slowing down the gold code. Stepping through your code with a debugger is the topic of chapter 4. This seemingly time-wasting trick will save lots of time debugging code that never worked quite right in the first place, but it has to be done when the code was written, and the ideas clear in the mind of the programmer, of course, not two years later. In chapter 5, Maguire makes an interesting comparison between function interfaces and candy-machine interfaces. Writing function interfaces in such a way as to be unambiguous is more important than apparent at first, and even the standard C library is rife with examples of poor interfaces. One example is getchar() which returns, of course, an int! Separating error codes from return values is but one piece of advice you will find in this chapter. Risk, and proper use of the language is the major topic of chapter 6. It is amazing how many programmers will code a straight-forward task in a quirky, non-portable way, only because it seems more clever or faster, when in fact it is neither. Discover other language tips in chapter 6. Resisting the temptation of writing clever, but obscure code is one of the focuses of chapter 7. Not referencing memory already freed is yet another tip, which seems all to obvious, but which many programmers do nevertheless. Treat input parameters as read-only, output-parameters as write- only, and so on. Not falling into the trap of thinking that tight C means tight machine code is yet another example. Use the debugger to get a feel for what code the compiler generates here. Finally, a little-realized aspect of development is that novice programmers tend to be the ones who maintain code, so experienced programmers should resist the temptation to write difficult to understand code, in all but the most extreme situations, since this code will not be understood, and thus most likely be ripped out by maintenance programmers. In the last chapter we are told to get the correct attitude to go along with all our new-found knowledge of bugs. Bugs never just disappear by themselves, for example, yet many programmers use this as an excuse. Do not add extra unnecessary features to products just because they are easy to add either, since this new (easy) code is yet another spot where bugs can hide and bite you from. This last chapter has many other great suggestions. The real strength of this book is that the tips themselves often sound silly, derogatory, and obvious, but in the context of the examples given, I came to realize again and again that I too had fallen for many of these traps. Read the whole book, page for page, and follow the advice given. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 8.3. Summary ═══ Summary This book is very important, and every serious developer should take a jet, not run, to the nearest bookstore to pick up a copy. It has an incredible array of methods for avoiding and fighting bugs, and in these bug- ridden, bloated code days, this is more important than ever before in history. Do not assume that you can do the things I mentioned in this review, and be done. They are only representative of what Maguire discusses. This books is so full of tricks and explanations that you just do not want to miss, and this book is really cheap and well worth the money. A+. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 8.4. Book Reviewed ═══ Book Reviewed  Writing Solid Code, Maguire - Microsoft Press, ISBN 1-55615-551-4, $US24.95, $32.95CAN, UK21.95. - Intended audience: C/C++ Programmers - Mark: A+ A great little cheap book on avoiding, fighting, and automatically preventing bugs. Every programmer in the world should own a copy of this great book, and should use it religiously. NOTES Please note that books aimed at different audiences should only be compared with great care, if at all. I intend to concentrate on the strong points of the books I review, but I will point out any weaknesses in a constructive manner. LEGEND BOOK: The name of the book, and the author(s) PUBLISHING INFORMATION: Publishing company, ISBN, and approximate price. AUDIENCE: This is a description of the audience I think the book targets best. This is not intended as gospel, just a guideline for people not familiar with the book. MARK: My opinion of the success of the book's presentation, and how well it targets its audience. Technical content, accuracy, organization, readability, and quality of index all weigh heavily here, but the single most important item is how well the book covers what it says it covers. Many books try to cover too much, and get a lower mark as a result. +------+-------------------------------------------------------------------------+ | MARK | DESCRIPTION | +------+-------------------------------------------------------------------------+ | A+ | Ground-breaking, all-around outstanding book | | A | Excellent book. This is what I want to see happen a lot | | A- | Excellent book with minor flaws | | B+ | Very good book with minor flaws or omissions | | B | Good book with some flaws and omissions | | B- | Good book, but in need of improvement | | C+ | Mediocre book with some potential, but in need of some updating | | C | Mediocre book with some good sections, but badly in need of fixing | | C- | Mediocre book, little good material, desperately in need of an overhaul | | D | Don't buy this book unless you need it, and nothing else exists | | F | Don't buy this book. Period | +------+-------------------------------------------------------------------------+ COMMENTS: This is a very brief summary of the review proper. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 8.5. Coming Up ═══ Coming Up My next mini review will be Code Complete. Other books I have: Code Complete, McConnell - The next book in this series from Microsoft Press Algorithms in C++, Sedgewick - This classic has a place in every C++ developer's library Zen of Code Optimization, Abrash - An exiting book about writing fast code the right way Other books I would like to review: Enough Rope to Shoot Yourself in the Foot, Holub Software Testing, Roper Error Coding Cookbook, Rorabaugh Applied Cryptography, Schneier Software Reuse, Karlsson If anyone has a book they want to see reviewed, I will be happy to oblige. Just mail me and tell me which. Publishers can send me books at the address on my personal page at the end of the magazine, and I will review all OS/2 development- related and advanced user books I receive. The Codesmith's Library - EDM/2 - Jul 1996 - Volume 4 Issue 6 ═══ 9. Contributors to this Issue ═══ Are You a Potential Author? We are always looking for authors. If you have a topic about which you would like to write, send a brief description of the topic electronically to any of the editors by the 15th of the month before the month in which your article will appear. This alerts us that you will be sending an article so that we can plan the issue layout accordingly. After you have done this, read the latest copy of the Article Submission Guidelines on our web site or download them to your machine also from our web site. These guidelines contain everything you need to know about writing an article for EDM/2. The editors can be reached at the following email addresses:  Larry Salomon - larry_salomon@iqpac.com (Internet).  Carsten Whimster - carsten_whimster@iqpac.com (Internet). The following people contributed to this issue in one form or another (in alphabetical order):  Stefan Ruck  Larry Salomon, Jr.  Brad Scharf  Carsten Whimster  Joe Wyatt  Gordon Zeglinski  Network distributors Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.1. Stefan Ruck ═══ Stefan Ruck Stefan Ruck is working as a PC programer under DOS in a small software company. He got in touch with computers about 15 years ago at school and started programing BASIC on a Commodore VIC20. He also did a little bit of assembler on this machine. Later on he learned COBOL and structured programing on IBM mainframes. Since about 6 years ago, he's been doing C programing on DOS PCs. After a short look at Windows 1 1/2 a year ago he started OS/2 programing in C++ in his spare time to learn a little bit more about GUIs and C++ and to see something new and different. He can be reached via CompuServe at 100664,1353 or via the Internet at 100664.1353@compuserve.com. Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.2. Larry Salomon, Jr. ═══ Larry Salomon Jr. Larry Salomon Jr. has been developing OS/2 applications since version 1.1 in 1989. He has written numerous applications, including the Scramble applet that was included in OS/2 versions 2.0-2.11, and the I-Brow, Magnify, and Screen Capture trio that has been distributed on numerous CD-ROMs. Larry is also the coauthor of the successful book, The Art of OS/2 2.1 C Programming (Wiley-QED). Finally, he is the CEO/President of IQPac Inc. which is responsible for the publication of EDM/2, and he is a frequent contributor to the publication. Larry can be reached electronically via the Internet at larry_salomon@iqpac.com. Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.3. Brad Scharf ═══ Brad Scharf Brad discovered computers while enrolled in a Chemistry B.Sc. program and soon switched to Computer Science. He has been using OS/2 since the release of version 2.0, has used OS/2 exclusively since version 2.1, and is now in the process of teaching himself PM programming. Brad can be reached via the Internet at brad_scharf@iqpac.com. Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.4. Carsten Whimster ═══ Carsten Whimster Carsten is an undergraduate Computer Science student at the University of Waterloo. He is currently in fourth year, and enjoying it immensely. He uses Visual Age C++, Watcom C/C++ 10.0a and Watcom VX-REXX 2.0c. Carsten is the author of some commandline utilities, and POV-Panel/2. He is also a TEAM-OS/2 member. You may reach Carsten... ...via email (Internet): carsten_whimster@iqpac.com ...via the World Wide Web: http://www.undergrad.math.uwaterloo.ca/~bcrwhims/ Please note the changed address and phone number ...via snail mail: Carsten Whimster 43 Wilhelm Street, apt. 2 Kitchener, Ontario Canada N2H 5R9 ...via voice: (519)749-2391 Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.5. Joe Wyatt ═══ Joe Wyatt Joe has been working in OS/2 since 1.3 and has programmed in Rexx on four different platforms. Rexx projects have included elegant, complex systems in both VM and OS/2, operating system automation in MVS, and your basic "quick and dirty" tools in DOS. He has recently mustered up the courage to jump into the world of consulting and self-employment. (God help him.) He makes his home in San Antonio, Texas with a wife and four children (along with 2 dogs, 1 cat, 1 turtle, varying numbers of fish, and a rabbit). He can be reached via internet address joe_wyatt@iqpac.com for comments and criticisms. Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.6. Gordon Zeglinski ═══ Gordon Zeglinski Gordon Zeglinski is a freelance programmer/consultant who received his Master's degree in Mechanical Engineering with a thesis on C++ sparse matrix objects. He has been programming in C++ for 6 years and also has a strong background in FORTRAN. He started developing OS/2 applications with version 2.0 . His current projects include a client/server communications program that utilitizes OS/2's features which has entered beta testing. Additionally, he is involved in the development of a "real-time" automated vehicle based on OS/2 and using C++ in which he does device driver development and designs the applications that comprise the control logic and user interface. He can be reached via the Internet at gordon_zeglinski@iqpac.com. Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6 ═══ 9.7. Network distributors ═══ Network Distributors These people are part of our distribution system to provide EDM/2 on networks other than the Internet. Their help to provide access to this magazine for others is voluntary and we appreciate them a lot!  Paul Hethmon (phethmon@utk.edu) - Compuserve  Gess Shankar (gess@knex.mind.org) - Internet  Jason B. Tiller (PeerGynt@aol.com) - America On-line  Jesper Nielsen (jesniels@internet.dk) - Denmark BBS's Gess is temporarily out of commission while he changes Internet domains. If you would like to become a "network distributor", be sure to contact the editors so that we can give you the credit you deserve! Contributors - EDM/2 - Jul 1996 - Volume 4, Issue 6